// xcloudtools
package main

import (
	"fmt"
	//"io"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"flag"

	"github.com/minio/minio-go"
)

const (
	endpoint        = "192.168.12.xx"
	accessKeyID     = "0F4DYP3V6V4J2KNxxxxxx"
	secretAccessKey = "Z3SUB3BktI7P55s7eRFIfofxxxx"
	useSSL          = false
)

var VERSION = ""

func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

//获取指定目录及所有子目录下的所有文件，可以匹配后缀过滤。
//func WalkDir(dirPth, suffix string) (files []string, err error) {
func WalkDir(dirPth string) (files []string, err error) {
	files = make([]string, 0, 30)
	//suffix = strings.ToUpper(suffix) //忽略后缀匹配的大小写

	err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, err error) error { //遍历目录
		//if err != nil { //忽略错误
		// return err
		//}

		if fi.IsDir() { // 忽略目录
			return nil
		}
		//if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
		//	files = append(files, filename)
		//}
		files = append(files, filename)

		return nil
	})

	return files, err
}

func PutObject(localfile, remotefile string, isdir bool) {
	localfile = strings.TrimRight(strings.TrimSpace(localfile), "/")
	remotefile = strings.TrimRight(strings.TrimSpace(remotefile), "/")

	minioClient, err := minio.New(endpoint, accessKeyID, secretAccessKey, useSSL)
	if err != nil {
		log.Fatalln(err)
	}

	mygroup := strings.Split(remotefile, "/")
	mybucket := mygroup[1]
	myobject := strings.Replace(remotefile, "/"+mybucket+"/", "", -1)

	location := ""

	err = minioClient.MakeBucket(mybucket, location)
	if err != nil {
		exists, err := minioClient.BucketExists(mybucket)
		if err == nil && exists {
			log.Printf("We already own %s\n", mybucket)
		} else {
			log.Fatalln(err)
		}
	}
	//log.Printf("Successfully created %s\n", mybucket)

	if isdir {

		if ok, _ := PathExists(localfile); ok {
			ListDir, _ := WalkDir(localfile)
			for _, xff := range ListDir {
				contentType := "application/octet-stream"
				dirobject := myobject + "/" + xff
				n, err := minioClient.FPutObject(mybucket, dirobject, xff, contentType)
				if err != nil {
					log.Fatalln(err)
				}
				log.Printf("Successfully uploaded %s of size %d\n", myobject, n)
			}

		}

	} else {
		for _, xfi := range strings.Split(localfile, "|") {
			if ok, _ := PathExists(xfi); ok {
				contentType := "application/zip" //application/octet-stream
				xff := strings.Split(xfi, "/")
				fobject := myobject + "/" + xff[len(xff)-1]
				n, err := minioClient.FPutObject(mybucket, fobject, xfi, contentType)
				if err != nil {
					log.Fatalln(err)
				}
				log.Printf("Success uploaded %s of size %d\n", fobject, n)
			} else {
				log.Printf("%s is not exit!", xfi)
				os.Exit(1)
			}
		}
	}

}

func GetObject(remotefile string, isdir bool) {
	remotefile = strings.TrimRight(strings.TrimSpace(remotefile), "/")
	minioClient, err := minio.New(endpoint, accessKeyID, secretAccessKey, useSSL)
	if err != nil {
		log.Fatalln(err)
	}

	if isdir {
		mygroup := strings.Split(remotefile, "/")
		mybucket := mygroup[1]
		for _, v := range ListObject(remotefile) {

			myobject := strings.Fields(v)[0]
			mylistgroup := strings.Split(myobject, "/")
			myfile := strings.Split(myobject, "/")[len(mylistgroup)-1]

			//fmt.Println(mybucket, myobject, myfile)
			err1 := minioClient.FGetObject(mybucket, myobject, myfile)
			if err1 != nil {
				fmt.Println(err1)
				return
			}
			fmt.Println(myobject + " Download Success")
		}

	} else {

		mygroup := strings.Split(remotefile, "/")
		mybucket := mygroup[1]
		myobject := strings.Replace(remotefile, "/"+mybucket+"/", "", -1)
		myfile := strings.Split(remotefile, "/")[len(mygroup)-1]

		//fmt.Println(mybucket, myobject, myfile)

		err1 := minioClient.FGetObject(mybucket, myobject, myfile)
		if err1 != nil {
			fmt.Println(err1)
			return
		}
		fmt.Println(myobject + " Download Success")
	}

}

func ListObject(remotefile string) (filelist []string) {

	remotefile = strings.TrimRight(strings.TrimSpace(remotefile), "/")

	s3Client, err := minio.New(endpoint, accessKeyID, secretAccessKey, useSSL)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Create a done channel to control 'ListObjects' go routine.
	doneCh := make(chan struct{})

	// Indicate to our routine to exit cleanly upon return.
	defer close(doneCh)

	// List all objects from a bucket-name with a matching prefix.
	mygroup := strings.Split(remotefile, "/")
	mybucket := mygroup[1]
	myobject := strings.Replace(remotefile, "/"+mybucket+"/", "", -1)
	for object := range s3Client.ListObjectsV2(mybucket, myobject, true, doneCh) {
		if object.Err != nil {
			fmt.Println(object.Err)
			return
		}
		filelist = append(filelist, strings.Fields(fmt.Sprintln(object))[1]+" "+strings.Fields(fmt.Sprintln(object))[6])
		//fmt.Println(strings.Fields(fmt.Sprintln(object))[1]+" "+strings.Fields(fmt.Sprintln(object))[6])
	}
	return
}

func DelObject(remotefile string, isdir bool) {

	remotefile = strings.TrimRight(strings.TrimSpace(remotefile), "/")

	minioClient, err := minio.New(endpoint, accessKeyID, secretAccessKey, useSSL)
	if err != nil {
		log.Fatalln(err)
	}
	mygroup := strings.Split(remotefile, "/")
	mybucket := mygroup[1]
	myobject := strings.Replace(remotefile, "/"+mybucket+"/", "", -1)

	if isdir {
		fmt.Println(myobject)
		doneCh := make(chan string)
		go func() {
			defer close(doneCh)
			for _, v := range ListObject(remotefile) {
				fmt.Println(strings.Fields(v)[0])
				doneCh <- strings.Fields(v)[0]
			}
		}()
		errorCh := minioClient.RemoveObjects(mybucket, doneCh)
		for e := range errorCh {
			fmt.Println("Error detected during deletion: " + e.Err.Error())
		}
		log.Println("DelSuccess")
	} else {
		err1 := minioClient.RemoveObject(mybucket, myobject)
		if err1 != nil {
			fmt.Println(err1)
			return
		}
		fmt.Println(myobject + " DelSuccess")
	}
}

func Help() {
	fmt.Println("v1.0")
	fmt.Println("author:kang")
	fmt.Println("Buildver:" + VERSION)
	fmt.Println("[options]:")
	fmt.Println("      -r                    bool type,use with other args:-p,-g,-d,-r=true")
	fmt.Println("      -g=                   get file form object storage;eg:-g=/pcauto-price/nginx/2017/06/03/xx.gz")
	fmt.Println("      -l=                   list dir; eg:-l=/pcauto-price/nginx/2017/06/03")
	//fmt.Println("      -d=                   delete file eg:-d=/pcauto-price/nginx/2017/06/03/xx.gz")
	fmt.Println("      -p=                   put file to object storage; example:-p=xx.gz /pcauto-price/nginx/2017/06/03")
	fmt.Println("      -h                    show help")
	fmt.Println("================Use Example==============")
	fmt.Println("	download file:      xcloudtools -g=/pcauto-price/nginx/2017/06/03/xx.gz")
	fmt.Println("	download dir:       xcloudtools -g=/pcauto-price/nginx/2017/06/03 -r")
	fmt.Println("	upload a file:      xcloudtools -p=xx.gz /pcauto-price/nginx/2017/06/03")
	fmt.Println("	upload more file:   xcloudtools -p=\"xx.gz|a.gz|b.gz\" /pcauto-price/nginx/2017/06/03")
	fmt.Println("	upload dir:         xcloudtools -r -p=a/b/c /pcauto-price/nginx/2017/06/03")
	fmt.Println("	list file:          xcloudtools -l=/pcauto-price/nginx/2017/06/03")
	//fmt.Println("	delete file:        xcloudtools -d=/pcauto-price/nginx/2017/06/03/xx.gz")
	//fmt.Println("	delete dir:         xcloudtools -d=/pcauto-price/nginx/2017/06/03 -r")

}

func main() {

	getobject := flag.String("g", "", "getobject")
	putobject := flag.String("p", "", "getobject")
	listobject := flag.String("l", "", "listobject")
	delobject := flag.String("d", "", "delobject")
	isdir := flag.Bool("r", false, "show help info")
	help := flag.Bool("h", false, "show help info")
	flag.Parse()

	if *help {
		Help()
		return
	}

	if *listobject != "" {
		for _, xfile := range ListObject(*listobject) {
			fff := strings.Fields(xfile)
			i, _ := strconv.Atoi(fff[1])
			if i > 102400 {
				fmt.Printf("%s %.2fMB\n", fff[0], float64(i)/1024/1024)
			} else {
				fmt.Printf("%s %.2fKB\n", fff[0], float64(i)/1024)
			}
		}
		return
	}

	if *getobject != "" {
		GetObject(*getobject, *isdir)
		return
	}

	if *delobject != "" {
		DelObject(*delobject, *isdir)
		return
	}

	if *putobject != "" && len(flag.Arg(0)) > 0 {
		PutObject(*putobject, flag.Arg(0), *isdir)
		return
	}

}
